#!/usr/bin/python
#-*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, division
import os
import random
import numpy as np
import sys
import json

from okex_websocket import Websocket
import logging
from time import sleep
import time
import pdb
import json
import traceback
import pandas as pd
import datetime
from datetime import timezone,timedelta
import requests
import math
import pickle
import copy
import gzip
import zlib

import binascii
import okex.consts as c 
import dateutil.parser as dp

import threading
import ctypes

class getOkexData(Websocket) :
    def __init__(self, endPoint, symbol, apiKey, apiSecret, passphrase):
        #tick交易数据
        self._trade = {}
        self._trade_tmp = {}
        #上一条tick交易数据
        self._pre_trade = {}
        #深度挂单数据
        self._quote = {}
        self._quote_tmp = {}
        #5档深度挂单数据
        self._quote5 = {}
        #订单数据
        self._order = {}
        #持仓数据
        self._position = {}
        self._position_detail = {}
        #上一交易时间(下单模块使用)
        self._order_pre_time = ''
        #上一交易时间(获取交易数据模块使用)
        self._trade_pre_time = ''
        #1分钟数据 
        self._trade_m1 = {}
        self._trade_m1_tmp = {}
        #每分钟计数
        self._minute_num = 0 
        #n分钟合成数据
        self._trade_mn = {}
        #用于合成15分钟数据的1分钟临时数据
        self._trade_m1_tmp_m15 = {}
        #15分钟合成数据
        self._trade_m15 = {}
        #下单状态:1.卖单挂单sell_resting_order,2.卖单追单sell_after_order,3.买单挂单buy_resting_order,4.买单追单buy_after_order,5.取消订单cancel_order
        self._order_status = ''
        #卖挂单时间
        self._sell_resting_order_timestamp = c.SELL_RESTING_ORDER_TIMESTAMP
        #卖追单时间
        self._sell_after_order_timestamp = c.SELL_AFTER_ORDER_TIMESTAMP
        self._sell_after_order_timestamp_add = c.SELL_AFTER_ORDER_TIMESTAMP_ADD
        self._sell_after_order_timestamp_add_15min = c.SELL_AFTER_ORDER_TIMESTAMP_ADD_15MIN
        #卖实时追单时间
        self._realtime_sell_after_order_timestamp = c.REALTIME_SELL_AFTER_ORDER_TIMESTAMP
        self._realtime_sell_after_order_timestamp_add = c.REALTIME_SELL_AFTER_ORDER_TIMESTAMP_ADD 
        self._realtime_sell_after_order_timestamp_add_15min = c.REALTIME_SELL_AFTER_ORDER_TIMESTAMP_ADD_15MIN 
        #买追单时间
        self._buy_after_order_timestamp = c.BUY_AFTER_ORDER_TIMESTAMP 
        self._buy_after_order_timestamp_add = c.BUY_AFTER_ORDER_TIMESTAMP_ADD  
        self._buy_after_order_timestamp_add_15min = c.BUY_AFTER_ORDER_TIMESTAMP_ADD_15MIN 
        #买实时追单时间
        self._realtime_buy_after_order_timestamp = c.REALTIME_BUY_AFTER_ORDER_TIMESTAMP 
        self._realtime_buy_after_order_timestamp_add = c.REALTIME_BUY_AFTER_ORDER_TIMESTAMP_ADD 
        self._realtime_buy_after_order_timestamp_add_15min = c.REALTIME_BUY_AFTER_ORDER_TIMESTAMP_ADD_15MIN 
        #取消订单时间
        self._cancel_order_timestamp = c.CANCEL_ORDER_TIMESTAMP 
        self._cancel_order_timestamp_add = c.CANCEL_ORDER_TIMESTAMP_ADD 
        self._cancel_order_timestamp_add_15min = c.CANCEL_ORDER_TIMESTAMP_ADD_15MIN 
        #目标字段
        self.MBAR_HEADER = ['datetime', 'symbol', 'open', 'high', 'low', 'close', 'vol']
        #OKEX接收字段
        self.OKEX_MBAR_HEADER = ['datetime', 'symbol', 'open', 'high', 'low', 'close', 'vol']

        #需要把继承父类的初始化放在子类初始化最后
        Websocket.__init__(self, endPoint, symbol, apiKey, apiSecret, passphrase)

    #消息驱动
    def _on_message(self, message):
        #
        message = inflate(message)
        self.logger.debug('message: %s-%s',message,type(message))

        '''Handler for parsing WS messages.'''
        #self.logger.setLevel(logging.DEBUG)  # Change this to DEBUG if you want a lot more info
        #pre_result = {}
        if type(message) == bytes :
            #if 'pong' in message :
            #    pdb.set_trace()
            #result = message
            result = str(message, encoding = "utf8") 
            if 'pong' in result:
                result = result
                #self.logger.debug('pong:%s,%s,%s' %(result,server_timestamp(),int(server_timestamp())))
            else :
                result = json.loads(result)
            self.logger.debug('result:%s',result)

            '''
                try:

                    if ('tick' in result) and ('data' in result['tick']):
                        if (result['ch'] in pre_result) and ('tick' in pre_result[result['ch']]) and result == pre_result[result['ch']]:
                            self.logger.info('完全重复结果: \r\n%s\r\n%s',result,pre_result[result['ch']])
                            return 
                        elif (result['ch'] in pre_result) and ('tick' in pre_result[result['ch']]) and result['tick'] == pre_result[result['ch']]['tick']:
                            self.logger.info('Tick重复结果: \r\n%s\r\n%s',result,pre_result[result['ch']])
                            return 
                        #elif (result['ch'] in pre_result) and ('tick' in pre_result[result['ch']]) and result['tick']['ts'] < pre_result[result['ch']]['tick']['ts']:
                        #    self.logger.info('小于前一条时间: \r\n%s\r\n%s',result,pre_result[result['ch']])
                        #    continue 
                        else :
                            pre_result[result['ch']] = result
                except Exception as re:
                    print('检测出异常{}'.format(result))
                    return
            '''
            message = result
        else :
            self.logger.info('error: %s' %(message))

        #数据处理模块
        self._get_trade_data(message)

    #数据处理模块
    def _get_trade_data(self, message):
        try:
            #sub trade,depth,order,account-订阅数据
            #{'event': 'subscribe', 'channel': 'spot/account:USDT'}
            if ('event' in message) and (message['event'] == 'subscribe') :
                self.logger.info("Subscribed to %s." % message['channel'])

            #pong-心跳数据
            elif ('pong' in message) :
                self.logger.debug("pong %s." % message)

            #auth-登陆数据 
            #{'event': 'login', 'success': True}
            elif ('event' in message) and (message['event'] == 'login') and ('success' in message) :
                if message['success'] == True:
                    self.logger.info("login: %s" %(message))

            #trade detail-交易数据
            #参数名	参数类型	描述
            #trade_id	String	成交id
            #price	String	成交价格
            #size	String	成交数量
            #side	String	成交方向（buy or sell）
            #timestamp	String	成交时间
            #instrument_id	String	币对
            #{"table": "spot/trade", "data": [{'instrument_id': 'BTC-USDT', 'price': '5133.1', 'side': 'sell', 'size': '0.02394361', 'timestamp': '2019-04-15T09:22:59.484Z', 'trade_id': '1315460352'}]}
            elif ('table' in message) and (message['table'] == 'spot/trade') and ('data' in message):
                #'insert'  - new row
                self.logger.debug('Tick-trade detail-insert:%s' % (message))
                for v in message['data']:
                    if 'instrument_id' in v :
                        m_symbol = v['instrument_id'] 
                        #判断每分钟开始时刻执行
                        m_datetime = v['timestamp'][0:10]+" "+v['timestamp'][11:17]+"00"

                        if self._trade_pre_time != '' and m_datetime < self._trade_pre_time :
                            self.logger.debug('Tick-trade detail-insert error data:%s-%s-%s' % (message, m_datetime, self._trade_pre_time))
                            m_datetime = self._trade_pre_time
                        
                        #判断最新消息分钟-上一消息分钟不等于0,为新的1分钟进行上一分钟合成
                        if self._trade_pre_time != '' and m_datetime >= self._trade_pre_time and int(m_datetime[14:16])-int(self._trade_pre_time[14:16]) != 0 and int(m_datetime[14:16]) in list(range(0,60,1)) :
                            self.logger.info('M1 _nowtime - _pertime: %s - %s' % (m_datetime, self._trade_pre_time))
                            #将当前分钟交易数据存入数据字典,插值处理
                            for _symbol in symbol_list :
                                #如果tick合成最新分钟存在数据
                                if _symbol in self._trade_tmp : 
                                    self._trade[_symbol] = self._trade_tmp[_symbol]
                                    self._trade[_symbol]['datetime'] = self._trade_pre_time[0:10]+" "+self._trade_pre_time[11:17]+"00"
                                    self._pre_trade[_symbol] = self._trade[_symbol]
                                elif _symbol in self._pre_trade:
                                    #如果tick合成最新分钟不存在数据，取上一次合成数据close作为ohlc数据，volume等于0
                                    self._trade[_symbol] = self._pre_trade[_symbol]
                                    self._trade[_symbol]['datetime'] = self._trade_pre_time[0:10]+" "+self._trade_pre_time[11:17]+"00"
                                    self._trade[_symbol]['open'] = self._pre_trade[_symbol]['close'] 
                                    self._trade[_symbol]['high'] = self._pre_trade[_symbol]['close'] 
                                    self._trade[_symbol]['low'] = self._pre_trade[_symbol]['close'] 
                                    self._trade[_symbol]['vol'] = 0 
                                else :
                                    self._trade[_symbol]['datetime'] = self._trade_pre_time[0:10]+" "+self._trade_pre_time[11:17]+"00"
                                    self._trade[_symbol]['open'] = 0 
                                    self._trade[_symbol]['high'] = 0 
                                    self._trade[_symbol]['low'] = 0 
                                    self._trade[_symbol]['vol'] = 0 

                                outPath_m1 = './log/m1/'
                                cn_timeStamp_m1 = datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M:%S.%f")
                                lc_timeStamp_m1 = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") 
                                data_m1_fileds = 'lc_timestamp,cn_timestamp,datetime,symbol,open,high,low,close,vol'
                                outFilename_m1 = self._trade[m_symbol]['datetime'][0:4] + self._trade[m_symbol]['datetime'][5:7] + self._trade[m_symbol]['datetime'][8:10] + '.csv' 
                                outFile_m1 = os.path.join(outPath_m1 ,outFilename_m1)
                                if not os.path.exists(outFile_m1):
                                    f = open(outFile_m1, 'w')
                                    print(data_m1_fileds,file=f)
                                else:
                                    f = open(outFile_m1, 'a')
                                print("%s,%s,%s,%s,%f,%f,%f,%f,%f" %(lc_timeStamp_m1,cn_timeStamp_m1,self._trade[_symbol]['datetime'],self._trade[_symbol]['symbol'],self._trade[_symbol]['open'],self._trade[_symbol]['high'],self._trade[_symbol]['low'],self._trade[_symbol]['close'],self._trade[_symbol]['vol']),file=f)

                            #清空tick合成1分钟暂存数据字典
                            self._trade_tmp.clear()

                            #当前时刻tick数据转换成分钟数据暂存
                            if m_symbol in self._trade_tmp :
                                self._trade_tmp[m_symbol] = self.tickToMinute(v,m_symbol)
                            else :
                                self._trade_tmp[m_symbol] = [] 
                                self._trade_tmp[m_symbol] = self.tickToMinute(v,m_symbol)
                        else :
                            if m_symbol in self._trade_tmp :
                                self._trade_tmp[m_symbol] = self.tickToMinute(v,m_symbol)
                            else :
                                self._trade_tmp[m_symbol] = [] 
                                self._trade_tmp[m_symbol] = self.tickToMinute(v,m_symbol)
                        self._trade_pre_time = m_datetime 
                        
                        outPath_tick = './log/tick/'
                        cn_timeStamp_tick = datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M:%S.%f")
                        lc_timeStamp_tick = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") 
                        data_tick = {'lc_timestamp':lc_timeStamp_tick,'cn_timestamp':cn_timeStamp_tick,'datetime':self._trade_tmp[m_symbol]['datetime'],'symbol':m_symbol,'price':self._trade_tmp[m_symbol]['close'],'vol':self._trade_tmp[m_symbol]['vol']}
                        outFilename_tick = self._trade_tmp[m_symbol]['datetime'][0:4] + self._trade_tmp[m_symbol]['datetime'][5:7] + self._trade_tmp[m_symbol]['datetime'][8:10] + '.csv' 
                        outFile_tick = os.path.join(outPath_tick,outFilename_tick)
                        if not os.path.exists(outFile_tick):
                            f = open(outFile_tick, 'w')
                        else:
                            f = open(outFile_tick, 'a')


                        if m_symbol in self._quote5 :
                            print({**data_tick,**self._quote5[m_symbol]},file=f)
                        else :
                            print({**data_tick,**{'bids':[],'asks':[]}},file=f)

            #depth step0-报价深度数据
            #参数名	参数类型	描述
            #price	String	价格
            #size	String	数量
            #timestamp	String	时间戳
            #num_orders	String	组成此条深度的订单数量
            #instrument_id	String	币对
            #checksum	String	检验和
            #["411.8","10",8][price ,size ,num_orders]
            #{'table': 'spot/depth', 'action': 'update', 'data': [{'instrument_id': 'LTC-USDT', 'asks': [['82.08', '88.799226', 4], ['82.11', '0.677', 1], ['82.13', '8.076', 3], ['82.14', '16.050318', 5], ['82.15', '67.495073', 8], ['82.16', '16.818211', 7], ['82.17', '16.420122', 6], ['82.2', '107.999859', 5], ['82.21', '0.57104', 3], ['82.25', '57.925016', 3], ['82.3', '90.258635', 5]], 'bids': [['82.05', '0.66', 1], ['82.01', '22.77656', 8], ['82', '52.793909', 11], ['81.99', '5.611773', 9], ['81.98', '33.03742', 22], ['81.97', '35.013163', 13], ['81.96', '58.14296', 15], ['81.95', '7.056106', 10], ['81.93', '115.95094', 12], ['81.91', '12.650235', 6], ['81.88', '11.570018', 9], ['81.87', '15.916077', 6], ['81.86', '39.694619', 2], ['81.85', '36.955308', 4]], 'timestamp': '2019-04-15T09:32:51.209Z', 'checksum': 1680391679}]}
            elif ('table' in message) and (message['table'] == 'spot/depth') and ('data' in message):
                self.logger.debug("bids-asks: %s" %(message))
                m_symbol = message['data'][0]['instrument_id']

                #局部全量数据 
                if ('action' in message) and (message['action'] == 'partial') :
                    self._quote[m_symbol] = {} 
                    self._quote[m_symbol] = {'bids':message['data'][0]['bids'],'asks':message['data'][0]['asks']} 
                    self._quote_tmp[m_symbol] = copy.deepcopy(self._quote[m_symbol])
                #增量数据
                elif ('action' in message) and (message['action'] == 'update') :
                    if m_symbol in self._quote:
                        self._quote_tmp[m_symbol] = copy.deepcopy(self._quote[m_symbol])
                        self.logger.debug("pre_bids-asks: %s" %(self._quote[m_symbol]))
                        #循环增量数据
                        for val_add in message['data'][0]['bids'] :
                            i = 0
                            j = 0
                            #循环当前全量数据
                            for val_all in self._quote[m_symbol]['bids'] :
                                #增量价格 > 全量bid价格,插入bid之前
                                if (float(val_add[0]) > float(val_all[0])) :
                                    self.logger.debug("bid_price_add>bid_price_all: %s-%s" %(val_add,val_all))
                                    self._quote[m_symbol]['bids'].insert(i,val_add)
                                    j = j+1
                                    break 
                                #增量价格 == 全量bid价格
                                elif (float(val_add[0]) == float(val_all[0])) :
                                    self.logger.debug("bid_price_add=bid_pirce_all: %s-%s" %(val_add,val_all))
                                    #数量 == 0,删掉全量此价格数据
                                    if float(val_add[1]) == 0 :
                                        self._quote[m_symbol]['bids'].remove(val_all)
                                    #数量 != 0,替换此价格数据
                                    else :
                                        self._quote[m_symbol]['bids'][i] = val_add
                                    j = j+1
                                    break 
                                i = i+1
                            if j == 0 :
                                self.logger.debug("bid_price_add<bidn_price_all: %s-%s" %(val_add,val_all))
                                self._quote[m_symbol]['bids'].insert(i,val_add)

                        for val_add in message['data'][0]['asks'] :
                            i = 0
                            j = 0
                            #循环当前全量数据
                            for val_all in self._quote[m_symbol]['asks'] :
                                #增量价格 < 全量ask价格,插入ask之前
                                if (float(val_add[0]) < float(val_all[0])) :
                                    self.logger.debug("ask_price_add<bid_price_all: %s-%s" %(val_add,val_all))
                                    self._quote[m_symbol]['asks'].insert(i,val_add)
                                    j = j+1
                                    break 
                                #增量价格 == 全量ask价格
                                elif (float(val_add[0]) == float(val_all[0])) :
                                    self.logger.debug("ask_price_add=ask_pirce_all: %s-%s" %(val_add,val_all))
                                    #数量 == 0,删掉全量此价格数据
                                    if float(val_add[1]) == 0 :
                                        self._quote[m_symbol]['asks'].remove(val_all)
                                    #数量 != 0,替换此价格数据
                                    else :
                                        self._quote[m_symbol]['asks'][i] = val_add
                                    j = j+1
                                    break 
                                i = i+1
                            if j == 0 :
                                self.logger.debug("ask_price_add>askn_price_all: %s-%s" %(val_add,val_all))
                                self._quote[m_symbol]['asks'].insert(i,val_add)
                    else :
                        self._quote[m_symbol] = {} 
                        self._quote[m_symbol] = {'bids':message['data'][0]['bids'],'asks':message['data'][0]['asks']} 
                        self._quote_tmp[m_symbol] = copy.deepcopy(self._quote[m_symbol])

                    self.logger.debug("modify self._quote[m_symbol]: %s" %(self._quote[m_symbol]))

                checksum = message['data'][0]['checksum']
                sum_bids_asks = ''
                for i in list(range(25)) :
                    if i == 0 :
                        sum_bids_asks = self._quote[m_symbol]['bids'][i][0]+":"+self._quote[m_symbol]['bids'][i][1]+":"+self._quote[m_symbol]['asks'][i][0]+":"+self._quote[m_symbol]['asks'][i][1]
                    else :
                        sum_bids_asks = sum_bids_asks+":"+self._quote[m_symbol]['bids'][i][0]+":"+self._quote[m_symbol]['bids'][i][1]+":"+self._quote[m_symbol]['asks'][i][0]+":"+self._quote[m_symbol]['asks'][i][1]
                checksum_diff = binascii.crc32(sum_bids_asks.encode())

                #会存在传递的checksum错误情况，因为修改后再次修改有可能正确
                if checksum != checksum_diff :
                    if checksum != ctypes.c_int32(checksum_diff).value :
                        self.logger.info("error sum_bids-sum_asks-checksum: %s-%s-%s-%s" %(m_symbol,sum_bids_asks,checksum,checksum_diff))
                        self.logger.debug("new self._quote[m_symbol]: %s-%s" %(m_symbol,self._quote[m_symbol]))
                        self.logger.debug("old self._quote[m_symbol]: %s-%s" %(m_symbol,self._quote_tmp[m_symbol]))
                    else :
                        self.logger.debug("python3-src32-sum_bids-sum_asks-checksum: %s-%s-%s-%s" %(m_symbol,sum_bids_asks,checksum,ctypes.c_int32(checksum_diff).value))
                else :
                    self.logger.debug("sum_bids-sum_asks-checksum: %s-%s-%s-%s" %(m_symbol,sum_bids_asks,checksum,checksum_diff))
                
                self.logger.debug("self._quote[m_symbol]-bids-asks: %s,%s" %(m_symbol, self._quote[m_symbol]))
            
            #公共-5档深度频道
            #返回参数
            #参数名	参数类型	描述
            #price	String	卖方深度
            #size	String	买方深度
            #timestamp	String	时间戳
            #num_orders	String	组成此条深度的订单数量
            #instrument_id	String	币对
            elif ('table' in message) and (message['table'] == 'spot/depth5') and ('data' in message):
                self.logger.debug("bids-asks: %s" %(message))
                m_symbol = message['data'][0]['instrument_id']
                if m_symbol in self._quote5:
                    self._quote5[m_symbol] = {'bids':message['data'][0]['bids'],'asks':message['data'][0]['asks']} 
                else :
                    self._quote5[m_symbol] = {} 
                    self._quote5[m_symbol] = {'bids':message['data'][0]['bids'],'asks':message['data'][0]['asks']} 
                #self.logger.info("self._quote5[m_symbol]-bids-asks: %s,%s" %(m_symbol, self._quote5[m_symbol]))

            #order-订单数据
            #参数名	类型	描述
            #order_id	String	订单ID
            #client_oid	String	由用户设置的订单ID
            #price	String	委托价格
            #size	String	委托数量（交易货币数量）
            #notional	String	买入金额，市价买入时返回
            #instrument_id	String	币对名称
            #side	String	buy or sell
            #type	String	limit,market(默认是limit)
            #timestamp	String	订单创建时间
            #filled_size	String	已成交数量
            #filled_notional	String	已成交金额
            #margin_trading	String	1 币币交易订单. 2 杠杆交易订单
            #order_type	String	0：普通委托 1：只做Maker（Post only） 2：全部成交或立即取消（FOK） 3：立即成交并取消剩余（IOC）
            #last_fill_px	String	最新成交价格 （如果没有，推0）
            #last_fill_qty	String	最新成交数量（如果没有，推0）
            #last_fill_time	String	最新成交时间（如果没有，推1970-01-01T00:00:00.000Z）
            #state	String	订单状态("-2":失败,"-1":撤单成功,"0":等待成交 ,"1":部分成交, "2":完全成交,"3":下单中,"4":撤单中,）
            #status为state旧版参数，会短期兼容，建议尽早切换state。
            #{'table': 'spot/order', 'data': [{'client_oid': '', 'filled_notional': '0', 'filled_size': '0', 'instrument_id': 'BTC-USDT', 'last_fill_px': '0', 'last_fill_qty': '0', 'last_fill_time': '1970-01-01T00:00:00.000Z', 'margin_trading': '1', 'notional': '', 'order_id': '2658394894578688', 'order_type': '0', 'price': '5132', 'side': 'buy', 'size': '0.00659918', 'status': 'open', 'timestamp': '2019-04-15T03:44:47.000Z', 'type': 'limit'}]}
            elif ('table' in message) and (message['table'] == 'spot/order') and ('data' in message):
                self.logger.info("Order: %s",message)
                #0-等待成交-open, 2-完全成交(终态)-filled, 1-部分成交-part_filled, -1-已撤销(终态)-cancelled ,-2-订单失败-failure 
                for v in message['data'] :
                    if (v) and ('instrument_id' in v) and ('order_id' in v) and ('status' in v) and ('timestamp' in v) :
                        #存储所有状态的订单信息
                        if v['instrument_id'] in self._order :
                            if int(v['order_id']) in self._order[v['instrument_id']] :
                                if (v['status'] in self._order[v['instrument_id']][int(v['order_id'])]) and (v['status'] != 'filled') :
                                    self._order[v['instrument_id']][int(v['order_id'])].update({(str(v['status'])+"_"+str(v['timestamp'])):v})
                                else :
                                    self._order[v['instrument_id']][int(v['order_id'])].update({v['status']:v})
                            else :
                                self._order[v['instrument_id']].update({int(v['order_id']):{v['status']:v}})
                        else: 
                            self._order.update({v['instrument_id']:{int(v['order_id']):{v['status']:v}}})

                    else :
                        self.logger.info("Order信息数据不全: %s" %(message))

                    self.logger.debug("self._order[v['instrument_id']]: %s-%s" %(v['instrument_id'],self._order[v['instrument_id']]))

                #实时信息需要使用self._order所以需要处理完成后使用
                #实时更新下单未成交量
                #_log_df['realtime_nofilled_volume'] = 0.0 
                #实时更新未下单成交量
                #_log_df['realtime_noopen_volume'] = 0.0 
                for v in message['data'] :
                    if (v) and ('instrument_id' in v) and ('order_id' in v) and ('status' in v) and ('timestamp' in v) :
                        #存储实时成交量信息
                        if ('filled' in v['status']) and ('realtime_change_volume' in _log_df) :
                            for (symbol,order_values) in self._order.items() :
                                _log_df.at[symbol,'realtime_change_volume'] = 0.0
                                for (order_id,order_value) in order_values.items() :
                                    _log_df.at[symbol,'tmp_realtime_change_volume'] = 0.0
                                    for (order_type,order_info) in order_value.items() :
                                        #filled in filled和part_filled
                                        #按照成交顺序订阅更新单一订单的已成交数量和已成交金额，filled_size已成交数量,filled_notional已成交金额
                                        if 'filled' in order_type :
                                            if ('side' in order_info) and (order_info['side'] == 'sell') :
                                                _log_df.at[symbol, 'tmp_realtime_change_volume'] = -float(order_info['filled_size'])
                                            elif ('side' in order_info) and (order_info['side'] == 'buy') : 
                                                _log_df.at[symbol, 'tmp_realtime_change_volume'] = float(order_info['filled_size'])
                                            else :
                                                logger.info('realtime filled-amount not sell-limit and not buy-limit')
                                    #同一币对多个订单的已成交数量和已成交金额加和
                                    _log_df.at[symbol, 'realtime_change_volume'] += _log_df.at[symbol, 'tmp_realtime_change_volume']
                            self.logger.debug('realtime model detail:\r\n %s' % (_log_df.T))

                        #存储实时下单未成交量
                        if ((v['status'] == 'open') or (v['status'] == 'cancelled') or ('filled' in v['status'])) and ('realtime_nofilled_volume' in _log_df) :
                            for (symbol,order_values) in self._order.items() :
                                _log_df.at[symbol,'realtime_nofilled_volume'] = 0.0
                                for (order_id,order_value) in order_values.items() :
                                    _log_df.at[symbol,'tmp_realtime_nofilled_volume'] = 0.0
                                    for (order_type,order_info) in order_value.items() :
                                        #filled in filled和part_filled
                                        #按照成交顺序订阅更新单一订单的已成交数量和已成交金额，filled_size已成交数量,filled_notional已成交金额
                                        if 'filled' in order_type :
                                            if ('side' in order_info) and (order_info['side'] == 'sell') :
                                                _log_df.at[symbol, 'tmp_realtime_nofilled_volume'] = -float(order_info['filled_size'])
                                            elif ('side' in order_info) and (order_info['side'] == 'buy') : 
                                                _log_df.at[symbol, 'tmp_realtime_nofilled_volume'] = float(order_info['filled_size'])
                                            else :
                                                logger.info('realtime filled-amount not sell-limit and not buy-limit')
                                    #同一币对多个订单的已成交数量和已成交金额加和
                                    _log_df.at[symbol, 'realtime_nofilled_volume'] += _log_df.at[symbol, 'tmp_realtime_nofilled_volume']
                            self.logger.debug('realtime model detail:\r\n %s' % (_log_df.T))

            #Position-order.match-持仓数据
            #{'table': 'spot/account', 'data': [{'balance': '136.781211684', 'available': '102.914219924', 'currency': 'USDT', 'id': '9196334', 'hold': '33.86699176'}]}
            #{'table': 'spot/account', 'data': [{'balance': '183.770262803', 'available': '183.770262803', 'currency': 'USDT', 'id': '9196334', 'hold': '0'}]}
            #{'table': 'spot/account', 'data': [{'balance': '0.00013148474', 'available': '0.00013148474', 'currency': 'BTC', 'id': '9196334', 'hold': '0'}]}
            #参数名	类型	描述
            #currency	String	币种
            #balance	String	余额
            #hold	String	冻结(不可用)
            #available	String	可用于交易或资金划转的数量
            #id	String	账户 id
            elif ('table' in message) and (message['table'] == 'spot/account') and ('data' in message):
                self.logger.info("Position: %s" %(message))
                for v in message['data']:
                    #根据下单状态进行控制
                    #下单状态self._order_status:卖单挂单sell_resting_order,卖单追单sell_after_order,买单挂单buy_resting_order,买单追单buy_after_order,取消订单cancel_order
                    if (quote_currency in self._position_detail) and (float(self._position_detail[quote_currency]['available']) != 0) and (float(v['available']) > float(self._position_detail[quote_currency]['available'])) and (self._order_status == 'buy_resting_order') :
                        self.logger.info("Position_detail buy_resting_order: %s" %(self._position_detail[quote_currency]))

                    self._position[v['currency']] = v['balance'] 
                    self._position_detail[v['currency']] = {'balance':v['balance'],'available':v['available'],'hold':v['hold']}
                    self.logger.debug("Position: %s,%s" %(v['currency'],v['balance']))
            else :
                #{'event': 'error', 'message': 'Internal system error', 'errorCode': 30043}
                #{'event': 'error', 'message': 'User not logged in / User must be logged in', 'errorCode': 30041}
                self.logger.info("other: %s" %(message))
        except Exception as e:
            self.logger.error(traceback.format_exc())
            self.logger.error(e)

    def tickToMinute(self,v,symbol) :
        _v = {}
        _v['datetime'] = v['timestamp']
        _v['symbol'] = symbol 
        if 'open' in self._trade_tmp[symbol]: 
            _v['open'] = float(self._trade_tmp[symbol]['open'])
        else :
            _v['open'] = float(v['price'])
        if 'high' in self._trade_tmp[symbol]: 
            if float(v['price']) > self._trade_tmp[symbol]['high'] :
                _v['high'] = float(v['price']) 
            else :
                _v['high'] = self._trade_tmp[symbol]['high']
        else :
            _v['high'] = float(v['price'])
        if 'low' in self._trade_tmp[symbol] :
            if float(v['price']) < self._trade_tmp[symbol]['low'] and float(v['price']) > 0:
                _v['low'] = float(v['price']) 
            else :
                _v['low'] = self._trade_tmp[symbol]['low']
        else :
            _v['low'] = float(v['price'])
        _v['close'] = float(v['price']) 
        if 'vol' in self._trade_tmp[symbol]:
            _v['vol'] = self._trade_tmp[symbol]['vol']+float(v['size'])
        else :
            _v['vol'] = float(v['size'])
        return _v

    #获取历史数据
    def getHistoryData(self,symbol_list) :
        for symbol in symbol_list :
            #每次最多获取200条，所以取15分钟数据来组合45分钟
            spotAPI = spot.SpotAPI(apiKey, apiSecret, passphrase, True)
            rest = spotAPI.get_kline(symbol, '', '', 900)

            if (not rest) :
                self.logger.info("spotAPI.get_kline(symbol, '', '', 900)-1: %s,%s" %(symbol,rest))
                sleep(2)
                spotAPI = spot.SpotAPI(apiKey, apiSecret, passphrase, True)
                rest = spotAPI.get_kline(symbol, '', '', 900)
                self.logger.info("spotAPI.get_kline(symbol, '', '', 900)-2: %s,%s" %(symbol,rest))

            df_m15 = pd.read_json(json.dumps(rest), orient='records')
            df_m15.columns = ['datetime','open','high','low','close','vol']
            df_m15['symbol'] = symbol
            df_m15['datetime'] = df_m15.datetime.str.replace('T',' ').str.replace('.000Z','') 
            df_m15.index = df_m15.datetime
            df_m15 = df_m15[self.OKEX_MBAR_HEADER]
            
            df_mn = self.convMxtoMn(df_m15,window_period)
            df_mn15 = self.convMxtoMn(df_m15,window_period_15min)

            #每次最多取200条，取1分钟数据
            spotAPI = spot.SpotAPI(apiKey, apiSecret, passphrase, True)
            rest = spotAPI.get_kline(symbol, '', '', 60)

            if (not rest) :
                self.logger.info("spotAPI.get_kline(symbol, '', '', 60)-1: %s,%s" %(symbol,rest))
                sleep(2)
                spotAPI = spot.SpotAPI(apiKey, apiSecret, passphrase, True)
                rest = spotAPI.get_kline(symbol, '', '', 60)
                self.logger.info("spotAPI.get_kline(symbol, '', '', 60)-2: %s,%s" %(symbol,rest))

            df_m1 = pd.read_json(json.dumps(rest), orient='records')
            df_m1.columns = ['datetime','open','high','low','close','vol']
            df_m1['symbol'] = symbol
            df_m1['datetime'] = df_m1.datetime.str.replace('T',' ').str.replace('.000Z','') 
            df_m1.index = df_m1.datetime
            df_m1 = df_m1[self.OKEX_MBAR_HEADER]
            
            #分钟数据最新一条为不完整数据,实时数据重复接收倒数第二条数据，所以从第二条取
            df_m1 = df_m1[df_m1.index<self._trade[symbol]['datetime']]

            #基础模型数据-合成45分钟数据
            if len(df_m1[df_m1.index >= str(df_mn.ix[-1].datetime)]) == period_number:
                self._trade_m1_tmp[symbol] = pd.DataFrame()
                self._trade_mn[symbol] = df_mn
            else :
                self._trade_m1_tmp[symbol] = df_m1[df_m1.index >= str(df_mn.ix[-1].datetime)]
                self._trade_mn[symbol] = df_mn.ix[:-1]
            self.logger.debug("symbol,self._trade_mn[symbol],self._trade_m1_tmp[symbol]: %s,%s,%s" %(symbol, self._trade_mn[symbol], self._trade_m1_tmp[symbol]))

            #高频数据模型-合成15分钟数据
            if len(df_m1[df_m1.index >= str(df_mn15.ix[-1].datetime)]) == period_number_15min:
                self._trade_m1_tmp_m15[symbol] = pd.DataFrame()
                self._trade_m15[symbol] = df_mn15
            else :
                self._trade_m1_tmp_m15[symbol] = df_m1[df_m1.index >= str(df_mn15.ix[-1].datetime)]
                self._trade_m15[symbol] = df_mn15.ix[:-1]
            self.logger.debug("symbol,self._trade_m15[symbol],self._trade_m1_tmp_m15[symbol]: %s,%s,%s" %(symbol, self._trade_m15[symbol], self._trade_m1_tmp_m15[symbol]))

    #将1分钟数据转换成n分钟数据
    def convMxtoMn(self,df_m1,period) :
        df_m1.index = pd.to_datetime(df_m1.datetime)
        on = df_m1['open'].resample(period, label='left', closed = 'left').first()
        hn = df_m1[['high']].resample(period, label='left', closed = 'left').max()
        ln = df_m1[['low']].resample(period, label='left', closed = 'left').min()
        cn = df_m1[['close', 'symbol']].resample(period, label='left', closed = 'left').last()
        sn = df_m1[['vol']].resample(period, label='left', closed = 'left').sum()
        df_mn = pd.concat([on, hn, ln, cn, sn], axis=1)
        df_mn = df_mn.dropna()
        df_mn['datetime'] = df_mn.index
        df_mn = df_mn[self.MBAR_HEADER]
        return df_mn

def run():
    # Instantiating the WS will make it connect. Be sure to add your api_key/api_secret.
    ws = getOkexData(endPoint, symbol_list, apiKey, apiSecret, passphrase)

def setup_logger():
    # Prints logger info to terminal
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)  # Change this to DEBUG if you want a lot more info
    ch = logging.StreamHandler()
    fh = logging.FileHandler('./log/okex_trade_7_'+ datetime.datetime.today().strftime('%Y%m%d') +'.log')
    # create formatter
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    formatter_fh = logging.Formatter("%(asctime)s : %(message)s")
    # add formatter to ch
    ch.setFormatter(formatter)
    fh.setFormatter(formatter_fh)
    logger.addHandler(ch)
    logger.addHandler(fh)
    return logger

#解压缩处理
def inflate(data):
    decompress = zlib.decompressobj(
            -zlib.MAX_WBITS  # see above
    )
    inflated = decompress.decompress(data)
    inflated += decompress.flush()
    return inflated

#获取服务器时间
def get_server_time():
    url = c.API_URL + c.SERVER_TIMESTAMP_URL
    response = requests.get(url)
    if response.status_code == 200:
        return response.json()['iso']
    else:
        return ""

#服务器时间转换成时间戳
def server_timestamp():
    server_time = get_server_time()
    parsed_t = dp.parse(server_time)
    timestamp = parsed_t.timestamp()
    return timestamp

if __name__ == "__main__":
    #设置dataframe显示宽度
    pd.set_option('display.width', 1000)  # 设置字符显示宽度
    pd.set_option('display.max_colwidth',1000)
    pd.set_option('display.max_rows',500) # 设置显示最大行
    pd.set_option('display.max_columns',500)

    logger = setup_logger()

    #endPoint = TRADE_URL 
    #apiKey = ACCESS_KEY 
    #apiSecret = SECRET_KEY 
    endPoint = c.ENDPOINT 
    apiKey = c.API_KEY 
    apiSecret = c.API_SECRET 
    passphrase = c.PASSPHRASE 

    #--init_model-------------------------------------------------------------------
    #交易货币list,eth以太币,eos柚子币,bch比特现金,trx波场币,xrp瑞波币,ltc莱特币,btc比特币
    #base_list = ['ETH','EOS','BCH','TRX','XRP','LTC','BTC']
    base_list = c.BASE_LIST 
    #计价货币,usdt美元代币
    quote_currency = c.QUOTE_CURRENCY 
    #计价货币持仓比例
    quote_currency_ratio = c.QUOTE_CURRENCY_RATIO 
    #点卡货币代码
    #point_currency = 'OKDK'
    #交易品种list,交易品种=交易货币+计价货币 
    symbol_list = []
    for bc in base_list :
        symbol_list.append(bc+"-"+quote_currency) 
    #品种list,第一位默认计价货币
    code_list = [quote_currency] + symbol_list
    #计价货币和交易品种的基础信息
    #symbols_confile = './config/okex_config.csv'
    symbols_confile = c.OK_SYMBOLS_CONFILE 
    #获取计价货币和交易品种的配置信息
    sDict = pd.read_csv(symbols_confile)

    #field_size 代表的是特征的维数 3 [high,low,close]
    #code_size 表示的品种个数 3 ['XRP','ETH','LTC'] 
    #window_size 代表的是时间窗的长度31个15分钟多品种交集时间数据
    #特征list,high最高价,low最低价,close收盘价
    field_list = c.FIELD_LIST 
    field_size = len(field_list) 
    code_size = len(code_list)-1 
    window_size = c.WINDOW_SIZE 

    #基础交易模型-单时间窗口时间长度 resample时间配置 周期时间+周期单位，例如：45分钟 '45min'
    period_number = c.PERIOD_NUMBER 
    period_unit = c.PERIOD_UNIT 
    window_period = str(period_number)+period_unit

    #高频交易模型-15分钟窗口数据
    period_number_15min = c.PERIOD_NUMBER_15MIN 
    period_unit_15min = c.PERIOD_UNIT_15MIN 
    window_period_15min = str(period_number_15min)+period_unit_15min

    #根据传递特征字段list，获取对应多品种的特征字段名称
    #根据close字段，获取对应多品种的特征字段名称
    code_fields_list = []
    code_close_list = []
    for _field in field_list :
        for _symbol in code_list:
            if _symbol != quote_currency :
                code_fields_list.append(_field + "_" + _symbol)
                if _field == 'close' :
                    code_close_list.append('close_' + _symbol)
            else :
                if _field == 'close' :
                    code_close_list.append('close')
    
    _log_df = pd.DataFrame()

    run()
